package lrscp.lib;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintStream;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Arrays;

import lrscp.lib.Log.LogOutputListener;

public class Log {
    private static ArrayList<LogOutputListener> lsnrs = new ArrayList<Log.LogOutputListener>();

    public static interface LogOutputListener {
        void flush();

        void onPrint(String log);
    }

    public static void addOutputListener(LogOutputListener lsnr) {
        if (lsnrs.contains(lsnr))
            return;
        lsnrs.add(lsnr);
    }

    public static void cls() {
        for (int i = 0; i < 50; i++) {
            System.out.print('\n');
        }
    }

    public static void d(Object log) {
        format(tag(), log, false);
    }

    public static void d(String tag, Object obj) {
        format(tag() + " <" + tag + "> ", obj, false);
    }

    private static void doPrint(Object obj) {
        obj = objectHandler(obj);
        System.out.print(obj);
        redirect(obj);
    }

    private static void doPrintln(Object obj) {
        obj = objectHandler(obj);
        System.out.println(obj);
        redirect(obj + "\n");
    }

    public static void e(Object log) {
        format(tag(), log, true);
    }

    public static void e(String tag, Object obj) {
        format(tag() + " <" + tag + "> ", obj, true);
    }

    public static void flush() {
        for (LogOutputListener lsnr : lsnrs) {
            if (lsnr != null)
                lsnr.flush();
        }
    }

    private static void format(String prefix, Object obj, boolean stderr) {
        obj = objectHandler(obj);
        PrintStream stream = stderr ? System.err : System.out;
        String s = "null";
        if (obj != null)
            s = obj.toString();
        StringReader reader = new StringReader(s);
        BufferedReader br = new BufferedReader(reader);
        String line = null;
        try {
            while ((line = br.readLine()) != null) {
                stream.println(prefix + line);
                redirect(prefix + line + "\n");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void i(Object log) {
        format(tag(), log, false);
    }

    public static void i(String tag, Object obj) {
        format(tag() + " <" + tag + "> ", obj, false);
    }

    public static void main(String[] args) {
        objectHandler(new int[] { 1, 2 });
    }

    public static Object objectHandler(Object obj) {
        if (obj == null)
            return null;
        if (obj instanceof int[]) {
            obj = Arrays.toString((int[]) obj);
        } else if (obj instanceof long[]) {
            obj = Arrays.toString((long[]) obj);
        } else if (obj instanceof boolean[]) {
            obj = Arrays.toString((boolean[]) obj);
        } else if (obj instanceof float[]) {
            obj = Arrays.toString((float[]) obj);
        } else if (obj instanceof double[]) {
            obj = Arrays.toString((double[]) obj);
        } else if (obj instanceof byte[]) {
            obj = Arrays.toString((byte[]) obj);
        } else if (obj instanceof Object[]) {
            obj = Arrays.toString((Object[]) obj);
        } else if (obj.getClass().isArray()) {
            Log.i("unsupport array type:" + obj.getClass().getCanonicalName());
        }
        return obj;
    }

    public static void p(Object log) {
        doPrintln(log);
    }

    public static void p(String tag, Object obj) {
        format(" <" + tag + "> ", obj, false);
    }

    public static void print(Object log) {
        doPrint(log);
    }

    public static void printConsoleProgress(int progress) {
        int width = 50;
        char sep = '+';
        char c;
        System.out.print("\r[");
        for (int i = 0; i < width; i++) {
            c = (i < progress * width / 100) ? sep : ' ';
            System.out.print(c);
        }
        System.out.print("]");
        System.out.print(progress + "%");
        if (progress >= 100) {
            System.out.print("\n");
        }
    }

    private static void redirect(Object obj) {
        for (LogOutputListener lsnr : lsnrs) {
            if (lsnr != null) {
                lsnr.onPrint(obj == null ? "null" : obj.toString());
            }
        }
    }

    public static void stdErrRedirect(String fileName) {
        try {
            System.setErr(new PrintStream(new File(fileName)));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static void stdOutRedirect(String fileName) {
        try {
            System.setOut(new PrintStream(new File(fileName)));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static void systemRedirect(String fileName) {
        try {
            PrintStream ps = new PrintStream(new File(fileName));
            System.setErr(ps);
            System.setOut(new PrintStream(ps));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }

    private static String tag() {
        String file = Thread.currentThread().getStackTrace()[3].getFileName();
        file = file.substring(0, file.lastIndexOf("."));
        String func = Thread.currentThread().getStackTrace()[3].getMethodName();
        int line = Thread.currentThread().getStackTrace()[3].getLineNumber();

        return String.format("%1$s.%3$s(%2$d): ", file, line, func);
    }

    public static void remove(LogOutputListener printListener) {
        lsnrs.remove(printListener);
    }
}
